#pragma once

#include <boost/detail/workaround.hpp>

# pragma warning(push)
# pragma warning(disable:4996)
# pragma warning(disable:4510)
# pragma warning(disable:4610)

#include <cstddef>
#include <stdexcept>
#include <boost/assert.hpp>
#include <boost/swap.hpp>

// Handles broken standard libraries better than <iterator>
#include <boost/detail/iterator.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>

// FIXES for broken compilers
#include <boost/config.hpp>

#include <string>
#include <sstream>


namespace util {

template<class T>
class Array {
private:
    T* elems;
    std::size_t N;
    std::size_t capality;
public:
    // type definitions
    typedef T              value_type;
    typedef T*             iterator;
    typedef const T*       const_iterator;
    typedef T&             reference;
    typedef const T&       const_reference;
    typedef std::size_t    size_type;
    typedef std::ptrdiff_t difference_type;

    Array(std::size_t len = 0, const value_type& val = value_type()):elems(0), N(0), capality(0) {
        resize(len, val);
    }
    virtual ~Array() {
        if(elems) {
            delete elems;
        }
    }
    bool resize(std::size_t newLen, const value_type& val = value_type()) {
        if(newLen > N) {
            if(newLen > capality) {
                T* p = new T[newLen];
                if(p) {
                    std::copy(begin(), end(), p);
                    std::fill(p + N, p + newLen, val);
                    N = capality = newLen;
                    if(elems) {
                        delete elems;
                    }
                    elems = p;
                } else {
                    return false;
                }
            } else {
                std::fill_n(elems + N, newLen - N, val);
                N = newLen;
            }
        } else {
            N = newLen;
        }
        return true;
    }
    // iterator support
    iterator begin() {
        if(!N) {
            return iterator( reinterpret_cast< T * >( this ) );
        }
        return elems;
    }
    const_iterator begin() const {
        if(!N) {
            return const_iterator(  reinterpret_cast< const T * >( this ) );
        }
        return elems;
    }
    iterator end() {
        if(!N) {
            return begin();
        }
        return elems+N;
    }
    const_iterator end() const {
        if(!N) {
            return begin();
        }
        return elems+N;
    }

    // reverse iterator support
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
    typedef std::reverse_iterator<iterator> reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
    // workaround for broken reverse_iterator in VC7
    typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
            reference, iterator, reference> > reverse_iterator;
    typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
            const_reference, iterator, reference> > const_reverse_iterator;
#elif defined(_RWSTD_NO_CLASS_PARTIAL_SPEC)
    typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
            value_type, reference, iterator, difference_type> reverse_iterator;
    typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
            value_type, const_reference, const_iterator, difference_type> const_reverse_iterator;
#else
    // workaround for broken reverse_iterator implementations
    typedef std::reverse_iterator<iterator,T> reverse_iterator;
    typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif

    reverse_iterator rbegin() {
        return reverse_iterator(end());
    }
    const_reverse_iterator rbegin() const {
        return const_reverse_iterator(end());
    }
    reverse_iterator rend() {
        return reverse_iterator(begin());
    }
    const_reverse_iterator rend() const {
        return const_reverse_iterator(begin());
    }

    // operator[]
    reference operator[](size_type i) {
        BOOST_ASSERT( i < N && "out of range" );
        return elems[i];
    }

    const_reference operator[](size_type i) const {
        BOOST_ASSERT( i < N && "out of range" );
        return elems[i];
    }

    // at() with range check
    reference at(size_type i) {
        rangecheck(i);
        return elems[i];
    }
    const_reference at(size_type i) const {
        rangecheck(i);
        return elems[i];
    }

    // front() and back()
    reference front() {
        rangecheck(0);
        return elems[0];
    }

    const_reference front() const {
        rangecheck(0);
        return elems[0];
    }

    reference back() {
        rangecheck(N-1);//if N==0, N-1 ==> FFFFFFFF, rangecheck failed!
        return elems[N-1];
    }

    const_reference back() const {
        rangecheck(N-1);
        return elems[N-1];
    }

    // size is constant
    size_type size() {
        return N;
    }
    bool empty() {
        return 0 == N;
    }
    size_type max_size() {
        return N;
    }

    // swap (note: linear complexity)
    void swap (Array<T>& y) {
        assert(N == y.N);
        for (size_type i = 0; i < N; ++i)
            boost::swap(elems[i],y.elems[i]);
    }

    // direct access to data (read-only)
    const T* data() const {
        return elems;
    }
    T* data() {
        return elems;
    }

    // use Array as C Array (direct read/write access to data)
    T* c_array() {
        return elems;
    }

    // assignment with type conversion
    template <typename T2>
    Array<T>& operator= (const Array<T2>& rhs) {
        assert(N >=rhs.N);
        std::copy(rhs.begin(),rhs.end(), begin());
        return *this;
    }

    // assign one value to all elements
    void assign (const T& value) {
        fill ( value );    // A synonym for fill
    }
    void fill   (const T& value) {
        std::fill_n(begin(),size(),value);
    }

    // check range (may be private because it is )
    void rangecheck (size_type i) {
        if (i >= size()) {
            std::out_of_range e("Array<>: index out of range");
            boost::throw_exception(e);
        }
    }
};

// comparisons
template<class T>
bool operator== (const Array<T>& x, const Array<T>& y) {
    return (x.N == y.N) && std::equal(x.begin(), x.end(), y.begin());
}
template<class T>
bool operator< (const Array<T>& x, const Array<T>& y) {
    return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
}
template<class T>
bool operator!= (const Array<T>& x, const Array<T>& y) {
    return !(x==y);
}
template<class T>
bool operator> (const Array<T>& x, const Array<T>& y) {
    return y<x;
}
template<class T>
bool operator<= (const Array<T>& x, const Array<T>& y) {
    return !(y<x);
}
template<class T>
bool operator>= (const Array<T>& x, const Array<T>& y) {
    return !(x<y);
}

// global swap()
template<class T>
inline void swap (Array<T>& x, Array<T>& y) {
    x.swap(y);
}

} /* namespace boost */


# pragma warning(pop)

//for log
#ifdef __SIMPLE_LOG__
template<typename T>
SimpleLog& operator <<(SimpleLog& slog, util::Array<T>& val){
	slog<<"Array:[";
	util::Array<T>::const_iterator it;
	for(it = val.begin(); it != val.end(); ++it){
		if(it != val.begin()){
			slog<<", ";
		}
		slog<<(*it);
	}
	slog<<"] ";
	return slog;
}
template<typename T>
SimpleLog& operator << (SimpleLog& slog, const util::Array<T>& val){
	return operator <<(slog, const_cast<util::Array<T>&>(val));
}
#endif


